package com.fs.luban.security.interceptor;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.StampedLock;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.StringUtils;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.util.CollectionUtils;

import com.fs.luban.common.Constant;
import com.fs.luban.module.sys.entity.SysResource;
import com.fs.luban.module.sys.service.SysResourceService;
import com.fs.luban.security.model.LubanConfigAttribute;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import lombok.extern.log4j.Log4j2;

/**
 * @title 鲁班自定义FilterInvocationSecurityMetadataSource
 * @Description 实现动态资源拦截匹配
 * @author fengshi
 */
@Log4j2
public class LubanFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {

	private SysResourceService sysResourceService;

	private final FilterInvocationSecurityMetadataSource securityMetadataSource;

	// 为了性能考虑受保护资源需要缓存
	private volatile Map<String, Set<ConfigAttribute>> configAttributesMap;

	// 读写锁
	private final StampedLock sl = new StampedLock();

	public void refresh() {
		log.info("刷新FilterInvocationSecurityMetadataSource开始");
		if (null == configAttributesMap) {
			configAttributesMap = Maps.newConcurrentMap();
		}
		// 重新获取受保护的资源
		List<SysResource> list = sysResourceService.queryAllRes();
		// 可能出现刷新的时候并发出现获取资源为空的情况
		Map<String, Set<ConfigAttribute>> newConfigAttributesMap = Maps.newConcurrentMap();
		if (!CollectionUtils.isEmpty(list)) {
			Set<ConfigAttribute> configAttributeSet = null;
			for (SysResource res : list) {
				configAttributeSet = Sets.newHashSet(new LubanConfigAttribute(res.getUrl(),
						Constant.SysResource.Methed.getNameByCode(res.getMethod())));
				newConfigAttributesMap.put(res.getUrl(), configAttributeSet);
			}
		}
		// 重新赋值受保护资源
		long stamp = sl.writeLock();
		try {
			this.configAttributesMap = newConfigAttributesMap;
		} finally {
			sl.unlock(stamp);
		}
		log.info("刷新FilterInvocationSecurityMetadataSource完成");
	}

	public LubanFilterInvocationSecurityMetadataSource(FilterInvocationSecurityMetadataSource securityMetadataSource,
			SysResourceService sysResourceService) {
		super();
		this.sysResourceService = sysResourceService;
		this.securityMetadataSource = securityMetadataSource;
		refresh();
	}

	@Override
	public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
		// 原始的调用方式
		Collection<ConfigAttribute> attributes = securityMetadataSource.getAttributes(object);
		final HttpServletRequest request = ((FilterInvocation) object).getRequest();
		String url = request.getRequestURI();
		// 处理可能出现带参数情况
		if (StringUtils.isNoneBlank(url) && url.indexOf("?") > 0) {
			url = url.substring(0, url.indexOf("?"));
		}
		while (sl.isWriteLocked()) {
			log.info("等待写操作结束...");
		}
		// 尝试直接获取受保护资源
		Collection<ConfigAttribute> temp = configAttributesMap.get(url);
		if (null == temp) {
			// 尝试ant匹配受保护资源
			AntPathRequestMatcher matcher;
			for (String resUrl : configAttributesMap.keySet()) {
				matcher = new AntPathRequestMatcher(resUrl, request.getMethod());
				if (matcher.matches(request)) {
					temp = configAttributesMap.get(resUrl);
					break;
				}
			}
		}
		if (null != temp) {
			attributes = temp;
		}
		return attributes;
	}

	@Override
	public Collection<ConfigAttribute> getAllConfigAttributes() {
		return securityMetadataSource.getAllConfigAttributes();
	}

	@Override
	public boolean supports(Class<?> clazz) {
		return securityMetadataSource.supports(clazz);
	}

}
